import os
import sys
import datetime
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from tkinter import font as tkfont
from typing import List, Dict, Any, Optional
import threading
import time

from src.search_engine import FileSearchEngine


class FileSearchUITk:
    """文件搜索应用的Tkinter界面"""
    
    def __init__(self, root):
        self.root = root
        self.search_engine = FileSearchEngine()
        self.search_timer = None
        self.result_data = []
        
        # 设置窗口
        self.root.title("本地文件搜索引擎")
        self.root.geometry("1000x700")
        self.setup_ui()
        
    def setup_ui(self):
        """初始化用户界面"""
        # 创建主框架
        self.main_frame = ttk.Frame(self.root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 搜索区域框架
        search_frame = ttk.LabelFrame(self.main_frame, text="搜索条件")
        search_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 目录选择行
        dir_frame = ttk.Frame(search_frame)
        dir_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Label(dir_frame, text="搜索目录:").pack(side=tk.LEFT, padx=5)
        self.dir_var = tk.StringVar()
        self.dir_entry = ttk.Entry(dir_frame, textvariable=self.dir_var, width=50)
        self.dir_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.dir_entry.configure(state="readonly")
        
        browse_btn = ttk.Button(dir_frame, text="浏览...", command=self.browse_directory)
        browse_btn.pack(side=tk.LEFT, padx=5)
        
        # 文件名和内容搜索行
        name_content_frame = ttk.Frame(search_frame)
        name_content_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Label(name_content_frame, text="文件名:").pack(side=tk.LEFT, padx=5)
        self.name_var = tk.StringVar()
        name_entry = ttk.Entry(name_content_frame, textvariable=self.name_var, width=25)
        name_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        
        ttk.Label(name_content_frame, text="文件内容:").pack(side=tk.LEFT, padx=5)
        self.content_var = tk.StringVar()
        content_entry = ttk.Entry(name_content_frame, textvariable=self.content_var, width=25)
        content_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        
        # 筛选条件行
        filter_frame = ttk.Frame(search_frame)
        filter_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 文件大小
        size_frame = ttk.LabelFrame(filter_frame, text="文件大小")
        size_frame.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        self.min_size_var = tk.IntVar()
        ttk.Label(size_frame, text="从:").pack(side=tk.LEFT, padx=5)
        min_size_spin = ttk.Spinbox(size_frame, from_=0, to=1000000, textvariable=self.min_size_var, width=8)
        min_size_spin.pack(side=tk.LEFT, padx=5)
        ttk.Label(size_frame, text="KB").pack(side=tk.LEFT)
        
        self.max_size_var = tk.IntVar()
        ttk.Label(size_frame, text="到:").pack(side=tk.LEFT, padx=5)
        max_size_spin = ttk.Spinbox(size_frame, from_=0, to=1000000, textvariable=self.max_size_var, width=8)
        max_size_spin.pack(side=tk.LEFT, padx=5)
        ttk.Label(size_frame, text="KB").pack(side=tk.LEFT)
        
        # 修改日期
        date_frame = ttk.LabelFrame(filter_frame, text="修改日期")
        date_frame.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        self.use_date_filter = tk.BooleanVar()
        date_check = ttk.Checkbutton(date_frame, text="启用日期筛选", variable=self.use_date_filter)
        date_check.pack(side=tk.LEFT, padx=5)
        
        ttk.Label(date_frame, text="从:").pack(side=tk.LEFT, padx=5)
        self.min_date = ttk.Entry(date_frame, width=10)
        self.min_date.insert(0, (datetime.datetime.now() - datetime.timedelta(days=365)).strftime("%Y-%m-%d"))
        self.min_date.pack(side=tk.LEFT, padx=5)
        
        ttk.Label(date_frame, text="到:").pack(side=tk.LEFT, padx=5)
        self.max_date = ttk.Entry(date_frame, width=10)
        self.max_date.insert(0, datetime.datetime.now().strftime("%Y-%m-%d"))
        self.max_date.pack(side=tk.LEFT, padx=5)
        
        # 文件类型
        type_frame = ttk.Frame(filter_frame)
        type_frame.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        ttk.Label(type_frame, text="文件类型:").pack(side=tk.LEFT, padx=5)
        self.file_type_var = tk.StringVar()
        file_types = [
            "所有文件",
            "文档",
            "图片",
            "音频",
            "视频",
            "压缩文件"
        ]
        self.file_type_combo = ttk.Combobox(type_frame, textvariable=self.file_type_var, values=file_types, width=10)
        self.file_type_combo.current(0)
        self.file_type_combo.pack(side=tk.LEFT, padx=5)
        
        # 按钮和进度条行
        btn_frame = ttk.Frame(search_frame)
        btn_frame.pack(fill=tk.X, padx=5, pady=5)
        
        self.search_btn = ttk.Button(btn_frame, text="开始搜索", command=self.start_search)
        self.search_btn.pack(side=tk.LEFT, padx=5)
        
        self.stop_btn = ttk.Button(btn_frame, text="停止", command=self.stop_search, state=tk.DISABLED)
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        
        self.progress_var = tk.IntVar()
        self.progress_bar = ttk.Progressbar(btn_frame, orient=tk.HORIZONTAL, length=100, mode='determinate', variable=self.progress_var)
        self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        
        self.status_label = ttk.Label(btn_frame, text="就绪")
        self.status_label.pack(side=tk.LEFT, padx=5)
        
        # 创建分隔面板
        paned = ttk.PanedWindow(self.main_frame, orient=tk.VERTICAL)
        paned.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 结果表格
        result_frame = ttk.Frame(paned)
        paned.add(result_frame, weight=3)
        
        # 创建Treeview用于显示结果
        columns = ('name', 'path', 'size', 'modified', 'type')
        self.result_tree = ttk.Treeview(result_frame, columns=columns, show='headings')
        
        # 定义列
        self.result_tree.heading('name', text='文件名')
        self.result_tree.heading('path', text='路径')
        self.result_tree.heading('size', text='大小')
        self.result_tree.heading('modified', text='修改日期')
        self.result_tree.heading('type', text='类型')
        
        # 设置列宽
        self.result_tree.column('name', width=150)
        self.result_tree.column('path', width=300)
        self.result_tree.column('size', width=100)
        self.result_tree.column('modified', width=150)
        self.result_tree.column('type', width=150)
        
        # 添加滚动条
        tree_scroll = ttk.Scrollbar(result_frame, orient=tk.VERTICAL, command=self.result_tree.yview)
        self.result_tree.configure(yscrollcommand=tree_scroll.set)
        
        # 放置组件
        tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.result_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 绑定双击事件
        self.result_tree.bind("<Double-1>", self.on_tree_double_click)
        # 绑定单击事件
        self.result_tree.bind("<<TreeviewSelect>>", self.on_tree_select)
        
        # 预览区域 - 使用选项卡
        notebook = ttk.Notebook(paned)
        paned.add(notebook, weight=1)
        
        # 文本预览标签页
        self.preview_frame = ttk.Frame(notebook)
        notebook.add(self.preview_frame, text="文本预览")
        
        self.text_preview = scrolledtext.ScrolledText(self.preview_frame, wrap=tk.WORD)
        self.text_preview.pack(fill=tk.BOTH, expand=True)
        self.text_preview.config(state=tk.DISABLED)
        
        # 统计信息标签页
        self.stats_frame = ttk.Frame(notebook)
        notebook.add(self.stats_frame, text="统计信息")
        
        self.stats_text = scrolledtext.ScrolledText(self.stats_frame, wrap=tk.WORD, height=10)
        self.stats_text.pack(fill=tk.BOTH, expand=True)
        self.stats_text.config(state=tk.DISABLED)
        
    def browse_directory(self):
        """选择搜索目录"""
        dir_path = filedialog.askdirectory(title="选择搜索目录")
        if dir_path:
            self.dir_var.set(dir_path)
    
    def get_file_type_list(self, selected_type):
        """根据选择的文件类型返回对应的MIME类型列表"""
        type_map = {
            "文档": ["text/plain", "application/pdf", "application/msword", 
                  "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
            "图片": ["image/jpeg", "image/png", "image/gif"],
            "音频": ["audio/mpeg", "audio/wav"],
            "视频": ["video/mp4", "video/quicktime"],
            "压缩文件": ["application/zip", "application/x-rar-compressed"]
        }
        return type_map.get(selected_type, None)
    
    def start_search(self):
        """开始搜索"""
        search_dir = self.dir_var.get()
        if not search_dir:
            messagebox.showwarning("警告", "请选择搜索目录")
            return
            
        # 获取搜索参数
        filename_pattern = self.name_var.get()
        content_pattern = self.content_var.get()
        
        # 文件大小筛选（转换为字节）
        min_size = self.min_size_var.get() * 1024 if self.min_size_var.get() > 0 else None
        max_size = self.max_size_var.get() * 1024 if self.max_size_var.get() > 0 else None
        
        # 日期筛选
        min_date = None
        max_date = None
        if self.use_date_filter.get():
            try:
                min_date = datetime.datetime.strptime(self.min_date.get(), "%Y-%m-%d")
                max_date = datetime.datetime.strptime(self.max_date.get(), "%Y-%m-%d")
                max_date = datetime.datetime.combine(max_date, datetime.time.max)  # 设置为当天结束时间
            except ValueError:
                messagebox.showwarning("警告", "日期格式无效，应为YYYY-MM-DD")
                return
        
        # 文件类型筛选
        file_types = None
        if self.file_type_var.get() != "所有文件":
            file_types = self.get_file_type_list(self.file_type_var.get())
        
        # 更新UI状态
        self.search_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)
        self.clear_results()
        self.progress_var.set(0)
        self.status_label.config(text="正在搜索...")
        
        # 开始搜索
        self.search_engine.search(
            root_dir=search_dir,
            filename_pattern=filename_pattern,
            content_pattern=content_pattern,
            min_size=min_size,
            max_size=max_size,
            min_date=min_date,
            max_date=max_date,
            file_types=file_types,
            progress_callback=self.update_progress
        )
        
        # 启动结果更新定时器
        self.search_timer = self.root.after(500, self.update_results)
    
    def stop_search(self):
        """停止搜索"""
        self.search_engine.stop_search()
        self.stop_btn.config(state=tk.DISABLED)
        self.search_btn.config(state=tk.NORMAL)
        self.status_label.config(text="搜索已停止")
        
    def update_progress(self, count: int, current_file: str):
        """更新进度条（此方法在搜索线程中调用）"""
        # 因为这个回调在非主线程中，用after方法将更新UI的操作调度到主线程
        self.root.after(0, self._update_progress_ui, count, current_file)
        
    def _update_progress_ui(self, count: int, current_file: str):
        """在主线程中更新UI进度"""
        self.progress_var.set(count)
        self.status_label.config(text=f"正在扫描: {current_file}")
        
    def update_results(self):
        """定时更新搜索结果"""
        if not self.search_engine.is_searching:
            # 搜索完成
            self.search_btn.config(state=tk.NORMAL)
            self.stop_btn.config(state=tk.DISABLED)
            self.status_label.config(text=f"搜索完成。找到 {len(self.search_engine.search_results)} 个结果。")
            
            # 更新结果表格
            self.display_results(self.search_engine.get_results())
            
            # 更新统计信息
            self.update_statistics(self.search_engine.get_results())
            
            # 停止定时器
            return
            
        # 搜索还在进行中，更新当前结果并继续定时器
        self.display_results(self.search_engine.get_results())
        self.search_timer = self.root.after(500, self.update_results)
        
    def clear_results(self):
        """清空结果表格"""
        for item in self.result_tree.get_children():
            self.result_tree.delete(item)
        self.result_data = []
        
        # 清空预览区
        self.text_preview.config(state=tk.NORMAL)
        self.text_preview.delete(1.0, tk.END)
        self.text_preview.config(state=tk.DISABLED)
        
        # 清空统计区
        self.stats_text.config(state=tk.NORMAL)
        self.stats_text.delete(1.0, tk.END)
        self.stats_text.config(state=tk.DISABLED)
    
    def display_results(self, results: List[Dict[str, Any]]):
        """显示搜索结果到表格"""
        # 记录已添加的数据以避免重复
        new_ids = set(id(r) for r in results)
        existing_ids = set(id(r) for r in self.result_data)
        
        # 只添加新数据
        for result in results:
            if id(result) not in existing_ids:
                # 格式化数据
                name = result['name']
                path = result['path']
                size = self.format_file_size(result['size'])
                modified = result['modified'].strftime("%Y-%m-%d %H:%M:%S")
                file_type = result['type']
                
                # 添加到表格
                self.result_tree.insert('', tk.END, values=(name, path, size, modified, file_type))
        
        # 更新记录的数据
        self.result_data = results.copy()
    
    def update_statistics(self, results: List[Dict[str, Any]]):
        """更新统计信息"""
        if not results:
            return
            
        # 计算统计数据
        total_size = sum(r['size'] for r in results)
        avg_size = total_size / len(results)
        
        # 按类型统计
        type_counts = {}
        for r in results:
            main_type = r['type'].split('/')[0]
            if main_type not in type_counts:
                type_counts[main_type] = 0
            type_counts[main_type] += 1
        
        # 格式化统计信息
        stats_text = f"搜索统计\n\n"
        stats_text += f"找到文件数: {len(results)}\n"
        stats_text += f"总大小: {self.format_file_size(total_size)}\n"
        stats_text += f"平均大小: {self.format_file_size(avg_size)}\n\n"
        stats_text += f"文件类型分布:\n"
        
        for t, count in type_counts.items():
            stats_text += f"- {t}: {count} 个文件 ({count/len(results)*100:.1f}%)\n"
        
        # 更新统计文本
        self.stats_text.config(state=tk.NORMAL)
        self.stats_text.delete(1.0, tk.END)
        self.stats_text.insert(tk.END, stats_text)
        self.stats_text.config(state=tk.DISABLED)
    
    def on_tree_select(self, event):
        """选择结果表格项时的处理"""
        selected_items = self.result_tree.selection()
        if not selected_items:
            return
            
        # 获取选中项数据
        item_id = selected_items[0]
        values = self.result_tree.item(item_id, 'values')
        
        if not values or len(values) < 5:
            return
            
        file_path = values[1]  # 路径是第二列
        file_type = values[4]  # 类型是第五列
        
        # 显示文本预览
        if file_type.startswith('text/') or file_type in ('application/json', 'application/xml', 'application/javascript'):
            try:
                with open(file_path, 'r', errors='ignore') as f:
                    content = f.read(10000)  # 最多读取10KB
                    if len(content) == 10000:
                        content += '\n\n[文件太大，只显示部分内容...]'
                
                self.text_preview.config(state=tk.NORMAL)
                self.text_preview.delete(1.0, tk.END)
                self.text_preview.insert(tk.END, content)
                self.text_preview.config(state=tk.DISABLED)
            except Exception as e:
                self.text_preview.config(state=tk.NORMAL)
                self.text_preview.delete(1.0, tk.END)
                self.text_preview.insert(tk.END, f"无法预览文件内容: {str(e)}")
                self.text_preview.config(state=tk.DISABLED)
        else:
            self.text_preview.config(state=tk.NORMAL)
            self.text_preview.delete(1.0, tk.END)
            self.text_preview.insert(tk.END, f"无法预览该类型的文件: {file_type}")
            self.text_preview.config(state=tk.DISABLED)
    
    def on_tree_double_click(self, event):
        """双击结果表格项时的处理"""
        item_id = self.result_tree.identify_row(event.y)
        if not item_id:
            return
            
        # 获取选中项数据
        values = self.result_tree.item(item_id, 'values')
        if not values or len(values) < 2:
            return
            
        file_path = values[1]  # 路径是第二列
        
        # 打开文件
        try:
            if sys.platform == 'win32':
                os.startfile(file_path)
            elif sys.platform == 'darwin':  # macOS
                import subprocess
                subprocess.call(['open', file_path])
            else:  # Linux
                import subprocess
                subprocess.call(['xdg-open', file_path])
        except Exception as e:
            messagebox.showerror("无法打开文件", f"打开文件失败: {str(e)}")
    
    @staticmethod
    def format_file_size(size_bytes: int) -> str:
        """格式化文件大小显示"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes/1024:.1f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes/(1024*1024):.1f} MB"
        else:
            return f"{size_bytes/(1024*1024*1024):.1f} GB" 